<?php

namespace app\common\service;
use app\common\model\mk\Spoils;
use think\Db;
use think\Exception;
use WxPayConfig;

require_once "vendor/wxpayAPI/lib/WxPay.Api.php";
require_once "vendor/wxpayAPI/example/WxPay.JsApiPay.php";

class WxpayService
{
//请求单次分账,此应用只有单次分账
    const API_URL_SINGLE = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing';

    //添加分账方
    const API_URL_ADD = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver';

    //删除分账方
    const API_URL_DEL = 'https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver';

    //服务商下单地址
    const API_URL_CREATE = 'https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi';

    protected $mchid;
    protected $appid;
    protected $apiKey;
    public $data = null;
    public function __construct($mchid = null, $appid = null, $apiKey = null)
    {
        $this->mchid = $mchid?$mchid:config("weChat")['mch_id'];     //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
        $this->appid = $appid?$appid:config("weChat")['appid']; //微信支付申请对应的公众号的APPID
        $this->apiKey = $apiKey?$apiKey:config('weChat')['key'];  //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
    }

    public function pay($storeId,$totalFee){
        $store = $order = Db::table('sms_mk_stores')
            ->where('id','=',$storeId)
            ->find();
        if (!@$store['spoils_no'] || !@$store['spoils_percent']){
            throw new Exception('该店铺还没有支付账户，请耐心等待!');
        }
        $spoilsFee = intval($totalFee * $store['spoils_percent']);
        $receivers = [
            [
                'type'=>'MERCHANT_ID', //MERCHANT_ID：商户号（mch_id或者sub_mch_id）PERSONAL_OPENID：个人openid
                'account'=>$store['spoils_no'],//分账接收方账号
                'amount'=>$spoilsFee,//分账金额
                'description'=>'您的收入'
                /*'name'=>'吴文艺',*/
            ]
        ];
        if($store['body_type'] == 'openid') $receivers[0]['type'] = 'PERSONAL_OPENID';
        $unified = array(
            'appid'         => $this->appid,
            'mch_id'        => $this->mchid,
            'nonce_str'     => self::createNonceStr(),
            'out_order_no'  => $orderNo,             //商户分账单号
            'transaction_id'=> $wxOrderNo,           //微信订单号
            /*'sign'          => '',*/
            'sign_type'     => 'HMAC-SHA256',
            'receivers'     => json_encode($receivers),
        );

        $unified['sign'] = $this->getSignSha256($unified,$this->apiKey);//签名

        $xmldata = $this->arrayToXml($unified);
        $xmlresult = $this->postXmlSSLCurl($xmldata,self::API_URL_SINGLE);
        $result = $this->xmlToArray($xmlresult);
        if (!$result){
            throw new Exception('分账失败');
        }
        if (@$result['result_code'] != 'SUCCESS'){
            throw new Exception($result['result_code']);
        } else {
            //分账信息保存
            $spoils = new Spoils();
            $spoils->order_id = $orderNo;
            $spoils->body_type = $store['body_type'];
            $spoils->totol_fee = $totalFee;
            $spoils->spoils_no = $store['spoils_no'];
            $spoils->spoils_percent = $store['spoils_percent'];
            $spoils->spoils_fee = $spoilsFee;
            $spoils->state = 'finished';
            $spoils->save();
            return $result;
        }
    }

    /**
     * 添加分账方
     */
    public function addSpoils($userAccount,$name='',$type = 'mchid'){
        if($type == 'mchid') $type = 'MERCHANT_ID';
        else $type = 'PERSONAL_OPENID';
        $receivers = [
                'type'=>$type, //MERCHANT_ID：商户号（mch_id或者sub_mch_id）PERSONAL_OPENID：个人openid
                'account'=>$userAccount,//分账接收方账号
                "relation_type"=> "STORE_OWNER"
            ];
        if($type == 'MERCHANT_ID'){
            $receivers['name'] = $name;
        }
        $unified = array(
            'mch_id'        => $this->mchid,
            'appid'         => $this->appid,
            'nonce_str'     => self::createNonceStr(),
            /*'sign'          => '',*/
            'sign_type'     => 'HMAC-SHA256',
            'receiver'     => json_encode($receivers),
        );

        $unified['sign'] = $this->getSignSha256($unified,$this->apiKey);//签名

        $xmldata = $this->arrayToXml($unified);
        $xmlresult = $this->postXmlSSLCurl($xmldata,self::API_URL_ADD);
        $result = $this->xmlToArray($xmlresult);
        if (!$result){
            throw new Exception('添加分账信息失败');
        }
        if(@$result['return_code'] == 'FAIL'){
            throw new Exception($result['return_msg']);
        }else if (@$result['result_code'] == 'FAIL'){
            throw new Exception($result['err_code_des']);
        } else {
            return $result;
        }
    }

    /**
     * 删除分账方
     */
    public function delSpoils(){

    }

    /**
     * 发起单次分账
     */
    public function spoilsSingle($orderNo,$wxOrderNo,$storeId,$totalFee){

        $store = $order = Db::table('sms_mk_stores')
            ->where('id','=',$storeId)
            ->find();
        if (!@$store['spoils_no'] || !@$store['spoils_percent']){
            throw new Exception('该店铺还没有分账账号，请先配置!');
        }
        $spoilsFee = intval($totalFee * $store['spoils_percent']);
        $receivers = [
            [
                'type'=>'MERCHANT_ID', //MERCHANT_ID：商户号（mch_id或者sub_mch_id）PERSONAL_OPENID：个人openid
                'account'=>$store['spoils_no'],//分账接收方账号
                'amount'=>$spoilsFee,//分账金额
                'description'=>'您的收入'
                /*'name'=>'吴文艺',*/
            ]
        ];
        if($store['body_type'] == 'openid') $receivers[0]['type'] = 'PERSONAL_OPENID';
        $unified = array(
            'appid'         => $this->appid,
            'mch_id'        => $this->mchid,
            'nonce_str'     => self::createNonceStr(),
            'out_order_no'  => $orderNo,             //商户分账单号
            'transaction_id'=> $wxOrderNo,           //微信订单号
            /*'sign'          => '',*/
            'sign_type'     => 'HMAC-SHA256',
            'receivers'     => json_encode($receivers),
        );

        $unified['sign'] = $this->getSignSha256($unified,$this->apiKey);//签名

        $xmldata = $this->arrayToXml($unified);
        $xmlresult = $this->postXmlSSLCurl($xmldata,self::API_URL_SINGLE);
        $result = $this->xmlToArray($xmlresult);
        if (!$result){
            throw new Exception('分账失败');
        }
        if (@$result['result_code'] != 'SUCCESS'){
            throw new Exception($result['result_code']);
        } else {
            //分账信息保存
            $spoils = new Spoils();
            $spoils->order_id = $orderNo;
            $spoils->body_type = $store['body_type'];
            $spoils->totol_fee = $totalFee;
            $spoils->spoils_no = $store['spoils_no'];
            $spoils->spoils_percent = $store['spoils_percent'];
            $spoils->spoils_fee = $spoilsFee;
            $spoils->state = 'finished';
            $spoils->save();
            return $result;
        }
    }

    /**
     * 退款
     * @param float $totalFee 订单金额 单位元
     * @param float $refundFee 退款金额 单位元
     * @param string $refundNo 退款单号
     * @param string $wxOrderNo 微信订单号
     * @param string $orderNo 商户订单号
     * @return string
     */
    public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo,$orderNo)
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        $unified = array(
            'appid' => $config['appid'],
            'mch_id' => $config['mch_id'],
            'nonce_str' => self::createNonceStr(),
            'total_fee' => intval($totalFee * 100),       //订单金额    单位 转为分
            'refund_fee' => intval($refundFee * 100),       //退款金额 单位 转为分
            'sign_type' => 'MD5',           //签名类型 支持HMAC-SHA256和MD5，默认为MD5
            'transaction_id'=>$wxOrderNo,               //微信订单号
            'out_trade_no'=>$orderNo,        //商户订单号
            'out_refund_no'=>$refundNo,        //商户退款单号
            'op_user_id'=>$config['mch_id'],  ////操作员 op_user_id .与商户号相同即可
            'refund_desc'=>'商品已售完',     //退款原因（选填）
        );
        $unified['sign'] = $this->getSign($unified,$config['key']);
        $xmldata = $this->arrayToXml($unified);
        $xmlresult = $this->postXmlSSLCurl($xmldata,'https://api.mch.weixin.qq.com/secapi/pay/refund');
        $result = $this->xmlToArray($xmlresult);
        if (!$result){
            $result_arr = array(
                'num'     =>      0,
                'desc'   =>      '退款失败',
            );
            return $result_arr;
        }
        if ($result['result_code'] != 'SUCCESS'){
            $result_arr = array(
                'num'     =>      2,
                'desc'    =>      $result['err_code_des'],
                'data'    =>      $result['result_code']
            );
        } else {
            $result_arr = array(
                'num'     =>      1,
                'desc'    =>      '退款成功！',
                'data'    =>      $result['return_code']
            );
        }

        return $result_arr;
    }
    public static function curlGet($url = '', $options = array())
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    /*
     * 生成随机字符串方法
     */
    protected function createNoncestr($length = 32 ){
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ ) {
            $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }


    /*
    * 对要发送到微信统一下单接口的数据进行签名
    */
    protected function getSign($Obj,$key){
        foreach ($Obj as $k => $v){
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$key;  //此处是商户key
        //签名步骤三：MD5加密
        $String = md5($String);
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        return $result_;
    }

    protected function getSignSha256($Obj,$key){
        foreach ($Obj as $k => $v){
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$key;  //此处是商户key
        //HMAC-SHA256签名方式：
        $sign=hash_hmac("sha256",$String,$key);
        //签名步骤四：所有字符转为大写
        $sign = strtoupper($sign);
        return $sign;
    }


    /*
      *排序并格式化参数方法，签名时需要使用
    */
    protected function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
                $v = urlencode($v);
            }
            //$buff .= strtolower($k) . "=" . $v . "&";
            $buff .= $k . "=" . $v . "&";
        }

        $reqPar = '';
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }


    //数组转字符串方法
    protected function arrayToXml($arr){
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    protected function xmlToArray($xml){
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }
    //需要使用证书的请求
    function postXmlSSLCurl($xml,$url,$second=30)
    {
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLCERT, APP_PATH .WxPayConfig::SSLCERT_PATH);
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLKEY, APP_PATH .WxPayConfig::SSLKEY_PATH);//处是证书存放地址，例：/home/apiclient_key.pem
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error" . "<br>";
            curl_close($ch);
            return false;
        }
    }


}


